/**
 * @file     GenericTraverser.h
 * @brief    Provides the interface to GenericTraverser class.
 *           This is traverses the AST tree.
 *
 * @author   Francisco Herrero
 * @email    francisco.herrero AT tallertechnologies.com
 *
 * @author   Marcos Diaz
 * @email    marcos.diaz AT tallertechnologies.com
 *
 * @author   Emanuel Ocampo
 * @email    emanuel.ocampo AT tallertechnologies.com
 *
 * Contents: Header file for Explorer AST Tree providing GenericTraverser interface.
 *
 * System:    traverser
 * Language:  C++
 *
 * @date      September 06, 2013
 *
 * This file is part of traverser.
 *
 * traverser is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * traverser is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with traverser. If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef PLUGIN_MEDIATOR_H
#define PLUGIN_MEDIATOR_H

#include <string>
#include "GenericVisitor.h"
#include "compilerapi/GenericTree.h"

namespace NSGppGeneric
{

class GenericTraverser;

/**
 * @brief Represents a traverser of the AST (Abstract Syntax Tree) for C++03.
 */
typedef GenericTraverser TraverserCppThree;

/**
 * @brief Represents a traverser of the AST for C++0x.
 */
typedef GenericTraverser TraverserCppEleven;

/**
 * @brief A class that traverses the AST tree.
 *
 */
class GenericTraverser
{
public:

    /**
     * @brief Sets the visitor and traverses the given namespace.
     *
     * @param ns this should be the global namespace to process.
     * @param visitor the visitor to set.
     */
    void traverse(const NSCompilerApi::GenericTree ns, IGenericVisitor* visitor);

private:
    /**
     *  @brief the visitor which will visit every type.
     *
     */
    IGenericVisitor* _visitor;
    /**
     * @brief obtains a float expression tree node and then calls visitor for him.
     *
     * @param fExpr tree node corresponding to float expression (cast to float from int, double or char)
     */
    void processFloatExpr(NSCompilerApi::GenericTree fExpr) const;

    /**
     * @brief obtains a fix trunc expression tree node and then calls visitor for him.
     *
     * @param ftExpr tree node corresponding to fix trunc expression (cast to int from float or double)
     */
    void processFixTruncExpr(NSCompilerApi::GenericTree ftExpr) const;

    /**
     * @brief obtains a nop expression tree node and then calls visitor for him.
     *
     * @param nopExpr tree node corresponding to nop expression (cast to char from int, double or float)
     */
    void processNopExpr(NSCompilerApi::GenericTree nopExpr) const;

    /**
     * @brief Checks if node.
     *
     * @param labelExpr tree node corresponding to label statement
     */
    void processLabelExpr(NSCompilerApi::GenericTree labelExpr) const;

    /**
     * @brief obtains a case label tree node and then calls visitor for him.
     * 
     * @param cLabelExpr tree node corresponding to case label statement
     */
    void processCaseLabelExpr(NSCompilerApi::GenericTree cLabelExpr) const;

    /**
     * @brief obtains a break tree node and then calls visitor for him.
     *
     * @param breakStmt tree node corresponding to break statement.
     */
    void processBreakStmt(NSCompilerApi::GenericTree breakStmt) const;

    /**
     * @brief obtains a return tree node and then calls visitor for him.
     *
     * @param returnExpr tree node corresponding to return statement
     */
    void processReturnExpr(NSCompilerApi::GenericTree returnExpr) const;

    /**
     * @brief obtains a continue tree node and then calls visitor for him.
     *
     * @param contStmt tree node corresponding to continue statement.
     */
    void processContinueStmt(NSCompilerApi::GenericTree contStmt) const;

    /**
     * @brief obtains a goto tree node and then calls visitor for him.
     *
     * @param gotoStmt tree node corresponding to goto statement
     */
    void processGotoExpr(NSCompilerApi::GenericTree gotoStmt) const;

    /**
     * @brief process and traverser a decl expression
     *
     * @param dExpr tree node corresponding to decl expression
     */
    void processDeclExpr(NSCompilerApi::GenericTree dExpr) const;

    /**
     * [processVarDecl description]
     * @param vDecl
     */
    void processVarDecl(NSCompilerApi::GenericTree vDecl) const;

    /**
     * @brief Returns the operator's kind (plus, minus, div, mult or other).
     *
     * @param operators the tree node corresponding to the operator from which we will get the kind.
     * @return the operator's kind
     */
    static IGenericVisitor::AssignmentOperators getAssignmentOp(const NSCompilerApi::GenericTree assignmetOp);

     /**
     * @brief Returns the operator's kind (plus, minus, div, mult or other).
     *
     * @param operators the tree node corresponding to the operator from which we will get the kind.
     * @return the operator's kind
     */
    static IGenericVisitor::BinaryOperators getBinaryOperator(const NSCompilerApi::GenericTree operators);

    /**
     * @brief Returns the body for any node.
     *
     * @param body the tree node
     * @param operand this a number of argument for the body
     */

    NSCompilerApi::GenericTree getOperandOfBody(NSCompilerApi::GenericTree body, const unsigned int operand) const;

    /**
     * @brief Returns the name declared for this node.
     *
     * @param decl the tree node corresponding to the declaration from which we will get the name
     * @param name the name that will be returned
     */
    static void getName(const NSCompilerApi::GenericTree decl, std::string& name);

    /**
     * @brief this will return true if the node represents something declared as constant.
     *
     * @param decl the tree node corresponding to the declaration from which we will get if it is declared constant or not.
     * @return true if decl is a constant, otherwise false
     */
    static bool isConstant(const NSCompilerApi::GenericTree decl);

    /**
     * @brief Gets the name of the type declared for a variable.
     *
     * @param decl the tree node corresponding to the declaration from which we will get the type name.
     * @param return Name the name of the returned type
     */
    static void getTypeName(const NSCompilerApi::GenericTree decl, std::string& returnName);

    /**
     * @brief Returns true if the node has a name that we dont want to process.
     *
     * @param decl the tree node corresponding to the declaration from which we will get the name to check.
     * @return true if decl is a reserved declaration, otherwise false
     */
    static bool isReservedDeclaration(const NSCompilerApi::GenericTree decl);

    /**
     * @brief Returns the visibility of a method or attribute (public, private or protected).
     *
     * @param decl the tree node corresponding to the declaration from which we will get the visibility.
     * @return the visibility of decl
     */
    static IGenericVisitor::AccessModifier getAccess(const NSCompilerApi::GenericTree decl);

    /**
     * @brief Processes and traverses the Parameters of a function or method
     *
     * @param decl the tree node corresponding to the function or method declaration we want to process.
     */
    void processParameters(const NSCompilerApi::GenericTree decl) const;

    /**
     * @brief Processes and traverses the attributes of a class, struct or union
     *
     * @param decl the tree node corresponding to the class, struct or union declaration we want to process.
     */
    void processAttributes(const NSCompilerApi::GenericTree decl) const;

    /**
     * @brief Processes and traverses the methods of a class ,struct or union
     *
     * @param decl the tree node corresponding to the class, struct or union declaration we want to process.
     */
    void processMethods(const NSCompilerApi::GenericTree decl) const;

    /**
     * @brief Processes and check an enum declaration or a typedef of an enum
     *
     * @param decl the tree node corresponding to the enum declaration we want to process.
     */
    void processEnumType(const NSCompilerApi::GenericTree decl) const;

    /**
     * @brief Processes and traverses the values of an enum
     *
     * @param decl the tree node corresponding to the enum declaration we want to process.
     */
    void processEnumValues(const NSCompilerApi::GenericTree decl) const;

    /**
     * @brief Processes and traverses any declaration
     *
     * This is the main function that checks the type of the declaration, and processes it.
     * It's called many times by other methods.
     * @param decl the tree node corresponding to the declaration we want to process.
     */
    void processDeclaration(const NSCompilerApi::GenericTree decl) const;

    /**
     * @brief Processes the operands of for, while and if statement
     *
     * @param body the tree node corresponding to the body we want to process.
     * @param operand maximum value of operands
     * @param count initial value of operands
     */
    void processOperands(NSCompilerApi::GenericTree body, const unsigned int operand, const unsigned int count) const;

    /**
    * @brief Processes and traverses any function statement
    *
    * Checks that this isn't an enum value(those are checked in the enum part), and then
    * separates between attributes(members of a class or a struct), union members, global constants, or common
    * variables.
    * @param funcStmt the tree node corresponding to the function statement we want to process.
    */
    void processFunctionStmt(NSCompilerApi::GenericTree funcStmt) const;
    /**
    * @brief Processes and traverses any if statement
    *
    * Checks that this is an IF_STMT, and then gets the body for later processing.
    * @param bodyIf the tree node corresponding to the if statement we want to process.
    */

    void processBodyIf(NSCompilerApi::GenericTree bodyIf) const;

    /**
    * @brief Processes and traverses any for statement
    *
    * Checks that this is an FOR_STMT, and then gets the body for later processing.
    * @param bodyFor the tree node corresponding to the for statement we want to process.
    */
    void processBodyFor(NSCompilerApi::GenericTree bodyFor) const;

    /**
    * @brief Processes and traverses any while statement
    *
    * Checks that this is an WHILE_STMT, and then gets the body for later processing.
    * @param bodyWhile the tree node corresponding to the while statement we want to process.
    */
    void processBodyWhile(NSCompilerApi::GenericTree bodyWhile) const;

    /**
    * @brief Processes and traverses any switch statement
    *
    * Checks that this is an SWITCH_STMT, and then gets the body for later processing.
    * @param bodySwitch the tree node corresponding to the switch statement we want to process.
    */
    void processBodySwitch(NSCompilerApi::GenericTree bodySwitch) const;

    /**
    * @brief Processes and traverses any main statement
    *
    * Process the current node from the statement's case
    * @param bodyStmt the tree node corresponding to the main statement we want to process.
    */
    void processBodyStmt(NSCompilerApi::GenericTree bodyStmt) const;

    /**
    * @brief Processes and traverses any statement list
    *
    * Checks that this is an STMT_LIST, and then walking every stmt.
    * @param stmtList the tree node corresponding to the statement list we want to process.
    */
    void processStmtList( NSCompilerApi::GenericTree stmtList) const;

    /**
    * @brief Processes and traverses any bind expresion
    *
    * Checks that this is an BIND_EXPR, and then gets the body for later processing. .
    * @param bExpr the tree node corresponding to the bind expresion we want to process.
    */
    void processBindExpr(NSCompilerApi::GenericTree bExpr) const;

    /**
    * @brief Processes and traverses any addres expresion
    *
    * Checks that this is an ADDR_EXPR, and then gets the body for later processing.
    * @param addExpr the tree node corresponding to the addres expresion we want to process.
    */
    void processAddExp(NSCompilerApi::GenericTree addExpr) const;

    /**
    * @brief Processes and traverses any call expresion
    *
    * Checks that this is an CALL_EXPR, and then gets the body for later processing.
    * @param callExpr the tree node corresponding to the call expresion we want to process.
    */
    void processCallExp(NSCompilerApi::GenericTree callExpr) const;

    /**
    * @brief Processes and traverses any convert expresion
    *
    * Checks that this is an CONVERT_EXPR, and then gets the body for later processing.
    * @param convExp the tree node corresponding to the convert expresion we want to process.
    */
    void processConverExpr(NSCompilerApi::GenericTree convExp) const;

    /**
    * @brief Processes and traverses any expresion statement
    *
    * Checks that this is an EXPR_STMT, and then gets the body for later processing.
    * @param exprStmt the tree node corresponding to the expresion statement we want to process.
    */
    void processExprStmt( NSCompilerApi::GenericTree exprStmt) const;

    /**
    * @brief Processes and traverses any cleanup point expresion
    *
    * Checks that this is an CLEANUP_POINT_EXPR, and then gets the body for later processing.
    * @param cpExpr the tree node corresponding to the cleanup point expresion we want to process.
    */
    void processCpExpr(NSCompilerApi::GenericTree cpExpr) const;

    /**
    * @brief Processes and traverses any operator binary and operator assignment
    *
    * Process of current node from bodyDefault 
    *
    * @param bodyDefault the tree node corresponding to the variable declaration we want to process.
    */
    void processDefault(NSCompilerApi::GenericTree bodyDefault) const;

    /**
    * @brief Processes and traverses any Init expresion
    *
    * Checks that this is an INIT_EXPR, and then gets the body for later processing.
    * @param bodyIexpr the tree node corresponding to the Init expresion we want to process.
    */
    void processInitExpr(NSCompilerApi::GenericTree bodyIexpr) const;

    /**
    * @brief Processes and traverses any modify expresion
    *
    * Checks that this is an MODIFY_EXPR, and then gets the body for later processing.
    * @param bodyMexpr the tree node corresponding to the modify expresion we want to process.
    */
    void proccesModifyExpr(NSCompilerApi::GenericTree bodyMexpr) const;

    /**
    * @brief Processes and traverses any Truth not expr
    *
    * Checks that this is an TRUTH_NOT_EXPR, and then gets the body for later processing.
    * @param bodyTruthNe the tree node corresponding to the truth not expresion we want to process.
    */
    void proccesBodyTnexpr(NSCompilerApi::GenericTree bodyTruthNe) const;

    /**
    * @brief Processes and traverses any Variable declaration
    *
    * Checks that this isn't an enum value(those are checked in the enum part), and then
    * separates between attributes(members of a class or a struct), union members, global constants, or common
    * variables.
    * @param decl the tree node corresponding to the variable declaration we want to process.
    */
    void processVariableDeclaration(const NSCompilerApi::GenericTree decl) const;

    /**
     * @brief Processes and traverses a Template declaration
     *
     * First, it checks the templetized class or function, and then checks the templates parameters.
     * @param decl the tree node corresponding to the template declaration we want to process.
     */
    void processTemplateDeclaration(const NSCompilerApi::GenericTree decl) const;

    /**
     * @brief Processes and traverses a Type declaration
     *
     * Here we separate between enum type declaration, typedef declared artificial (those are generated by the
     * compiler and includes class, struct and union declaration), and common typedefs.
     * @param decl the tree node corresponding to the type declaration we want to process.
     */
    void processType(const NSCompilerApi::GenericTree decl) const;

    /**
     * @brief Separates between a Record declaration(struct or class), and a Union declaration
     *
     * @param decl the tree node corresponding to the union or record we want to process.
     * @param name the name declared.
     */
    void unionOrRecord(const NSCompilerApi::GenericTree decl, const NSCompilerApi::GenericTree type, std::string& name) const;

    /**
     * @brief Processes and traverses an artificial Type declaration
     *
     * First, it takes the declaration (to see if it is a union, a struct or a class), then checks if it is a
     * template parameter; if so, it treats the declaration as a typedef, it processes the attributes and methods otherwise.
     *
     * @param decl the tree node corresponding to the union or record we want to process.
     */
    void processTypeDeclArtificial(const NSCompilerApi::GenericTree decl) const;

    /**
     * @brief Separates between a struct or class declaration and visits it.
     *
     * @param decl the tree node corresponding to the record declaration we want to process.
     */
    void processRecordDeclaration(const NSCompilerApi::GenericTree decl) const;

    /**
     * @brief Separates between a function or method declaration and visits it.
     *
     * @param decl the tree node corresponding to the function or method declaration we want to process.
     * @param name the name declared
     */
    void functionOrMethod(const NSCompilerApi::GenericTree decl, std::string& name) const;

    /**
     * @brief Processes and traverses a function or method
     *
     * first we separate if its a function or method, then we process its parameters and its body.
     * @param funcDecl the tree node corresponding to the function declaration we want to process.
     */
    void processFunction(const NSCompilerApi::GenericTree funcDecl) const;

    /**
     * @brief Processes the body of a function taking the declarations inside of it.
     *
     * @param decl the tree node corresponding to the function declaration we want to process.
     */
    void processFunctionBody(const NSCompilerApi::GenericTree decl) const;

    /**
     * @brief Takes the subblocks of a block and processes them.
     *
     * @param Blocks the tree node corresponding to the block we want to process.
     */
    void processSubblocks(const NSCompilerApi::GenericTree block) const;

    /**
     * @brief Processes the block, the declarations inside them and the subblocks.
     *
     * @param decl the tree node corresponding to the block we want to process.
     */
    void processBlock(const NSCompilerApi::GenericTree decl) const;

    /**
     * @brief Processes the declarations inside a block.
     *
     * @param decl the tree node corresponding to the block we want to process.
     */
    void processBlockVariables(const NSCompilerApi::GenericTree decl) const;

    void test_Method(const NSCompilerApi::GenericTree decl) const;

    /**
     * @brief Traverses the given namespace and separates the declaration of namespaces and the other declarations inside it
     *
     * @param ns the tree node we want to process.
     */
    void traverse(const NSCompilerApi::GenericTree ns) const;

    /**
     * @brief Processes the namespace's name and traverses it.
     *
     * @param level the tree node corresponding to the namespace we want to process.
     */
    void traverseNamespaces(const NSCompilerApi::GenericTree level) const;

    /**
     * @brief Takes the declarations in a namespace and processes them.
     *
     * @param level the tree node corresponding to the namespace we want to process.
     */
    void traverseDeclarations(const NSCompilerApi::GenericTree level) const;

    /**
     * @brief Checks if the declaration is of an array and add to the typename "_array", as many times
     * as dimensions has the array.
     *
     * @param decl the tree node corresponding to the declaration we want to process.
     * @param typeName the type name it could modify.
     */
    static void checkArrayTypeName(NSCompilerApi::GenericTree decl, std::string& typeName);

    /**
     * @brief Checks if the declaration is a reference and add to the typename "_ref".
     *
     * @param decl the tree node corresponding to the declaration we want to process.
     * @param typeName the type name it could modify.
     */
    static void checkReferenceTypeName(NSCompilerApi::GenericTree decl, std::string& typeName);

    /**
     * @brief Checks if the declaration is of pointer and add to the typename "_ptr", as many times
     * as indirections the variable has.
     *
     * @param decl the tree node corresponding to the declaration we want to process.
     * @param typeName the type name it could modify.
     */
    static void checkPointerTypeName(NSCompilerApi::GenericTree decl, std::string& typeName);

    static const std::string RESERVED_DECLARATION;
    /**
    * Constant of call_expr
    */

    static const unsigned int  CALL_OPERAND = 1u;

    /**
    * Constant of process_default
    */

    static const unsigned int  OPERAND_DEFAULT_FIRST = 0u;
    static const unsigned int  OPERAND_DEFAULT_SECOND = 1u;

    /**
    * Constant of init_expr
    */

    static const unsigned int INIT_EXPR_OPERAND = 1u;

    /**
    * Constant of modify_expr
    */

    static const unsigned int MOD_EXPR_OPERAND = 1u;

    /**
    * Constant of conver_expr
    */

    static const unsigned int CONV_EXPR_OPERAND = 0u;

    /**
    * Constant of expr_stmt
    */

    static const unsigned int EXPR_STMT_OPERAND = 0u;

    /**
    * Constant of cleanup_expr
    */

    static const unsigned int CP_EXPR_OPERAND = 0u;

    /**
    * Constant of bind_expr
    */

    static const unsigned int BODY_OPERAND = 1u;

    /**
    * Constant of switch_expr
    */

    static const unsigned int SWITCH_OPERAND = 1u;

    /**
    * Constant of truth_no_expr
    */

    static const unsigned int TRUTH_N_EXPR_OPERAND = 0u;

    /**
    *  Constant of for
    */

    static const unsigned int BOUND_FOR = 4u;
    static const unsigned int INITIAL_FOR = 1u;
    static const unsigned int INITIALIZATION = 0u;
    static const unsigned int CONDITION_FOR = 1u;
    static const unsigned int INCREMENT = 2u;
    static const unsigned int BODY_FOR = 2u;

    /**
    *  Constant of while
    */

    static const unsigned int BOUND_WHILE = 2u;
    static const unsigned int INITIAL_WHILE = 0u;
    static const unsigned int CONDITION_WHILE = 0u;
    static const unsigned int BODY_WHILE = 1u;

    /**
    *  Constant of if
    */

    static const unsigned int BOUND_IF = 2u;
    static const unsigned int INITIAL_IF = 0u;
    static const unsigned int CONDITION_IF = 0u;
    static const unsigned int TRUE_PART = 1u;
    static const unsigned int ELSE_PART = 2u;

    /**
    * Constant of BinOperator
    */

    static const unsigned int LEFT_OPERATOR = 0u;
    static const unsigned int RIGHT_OPERATOR = 1u;

    /**
    * Constant of AssignmentOperator
    */

    static const unsigned int LEFT_VALUE = 0u;
    static const unsigned int RIGHT_VALUE = 1u;

    /**
    * Constant of CaseLabelExpr
    */

    // static const unsigned int BODY_CASE_LABEL = 2u;

    /**
    * Constant of returnExpr
    */

    static const unsigned int BODY_RETURN_EXPR = 0u;

    /**
    * Constant of gotoExpr
    */

    static const unsigned int LABEL_GOTO_EXPR = 0u;

    static const unsigned int BODY_OPERAND_BODY = 0u;
};

#define GENERIC_TRAVERSER_INLINE_H
#include "GenericTraverserInline.h"
#undef GENERIC_TRAVERSER_INLINE_H

} // end namespace

#endif
